package com.aphrodite.ui.graphics;

import javax.microedition.lcdui.Graphics;

import com.aphrodite.image.ColorUtil;

public final class GraphicEffects {
	/**
	 * Draw a horizontal or vertical gradient with the lower end colors used in
	 * the center of the rectangle
	 * <p>
	 * <code>startColor</code>: 0xFF6300<br/> <code>endColor</code>: 0xFFF700
	 * <table>
	 * <tr>
	 * <td><img src="../../../resources/vcGrad.png">
	 * <td>
	 * <td><img src="../../../resources/hcGrad.png">
	 * <td>
	 * </tr>
	 * <tr>
	 * <td><code>isVertical</code>: true</td>
	 * <td><code>isVertical</code>: false</td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param g
	 * @param startColor
	 * @param endColor
	 * @param isVertical
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 */
	public final static void drawCenteredGradientRect(final Graphics g, final int startColor, final int endColor,
			boolean isVertical, final int x, final int y, final int width, final int height) {
		int wHalf = width / 2;
		int hHalf = height / 2;
		drawGradientRect(g, startColor, endColor, isVertical, x, y, isVertical ? wHalf : width, isVertical ? height
				: hHalf);
		drawGradientRect(g, endColor, startColor, isVertical, isVertical ? x + wHalf : x, isVertical ? y : y + hHalf,
				isVertical ? wHalf : width, isVertical ? height : hHalf);
	}

	/**
	 * Draw Centererd gradient with alpha processing.
	 * 
	 * @see GraphicEffects#drawCenteredGradientRect(Graphics, int, int, boolean,
	 *      int, int, int, int)
	 * @param g
	 * @param startColor
	 * @param endColor
	 * @param alphaLevel
	 * @param isVertical
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 */
	public final static void drawCenteredGradientRect(final Graphics g, final int startColor, final int endColor,
			final int alphaLevel, boolean isVertical, final int x, final int y, final int width, final int height) {
		int wHalf = width / 2;
		int hHalf = height / 2;
		drawGradientRect(g, startColor, endColor, alphaLevel, isVertical, x, y, isVertical ? wHalf : width,
				isVertical ? height : hHalf);
		drawGradientRect(g, endColor, startColor, alphaLevel, isVertical, isVertical ? x + wHalf : x, isVertical ? y
				: y + hHalf, isVertical ? wHalf : width, isVertical ? height : hHalf);
	}

	private static void drawCircGradRect(Graphics g, int[] gradient, int x, int y, int width, int height) {
		for (int i = 0; i < gradient.length; i++) {
			g.setColor(gradient[i]);
			g.fillRect(x + i, y + i, width - i * 2, height - i * 2);
		}
	}

	/**
	 * Draw a circular gradient
	 * <p>
	 * The following is a circular gradient with <code>startColor </code>
	 * 0xff0000 and <code>endColor </code>0x00ff00 with a <code>radius </code>
	 * of 50<br/> <img src="../../../resources/circulargrad.png">
	 * </p>
	 * 
	 * @param g
	 *            Graphics primitive
	 * @param startColor
	 * @param endColor
	 * @param x
	 *            point of the gradient center
	 * @param y
	 *            point of the gradient center
	 * @param radius
	 *            of the gradient circle
	 */
	public final static void drawCircularGradient(final Graphics g, final int startColor, final int endColor,
			final int x, final int y, final int radius) {
		final int[] color = com.aphrodite.image.ColorUtil.getGradient(startColor, endColor, radius);
		drawCircularGradient(g, color, x, y, radius);
		// g.setColor(color.length - 1);
		// drawCircle(g, x, y, radius);
	}

	private final static void drawCircularGradient(final Graphics g, int[] color, int x, int y, int radius) {
		for (int i = 0; i < radius; i++) {
			g.setColor(color[i]);
			g.drawArc(x - i / 2, y - i / 2, i, i, 0, 360);
			g.drawArc((x - i / 2) - 1, (y - i / 2) - 1, i, i, 0, 360);
		}
		g.setColor(color[0]);
		g.fillRect(x, y, 1, 1);
		g.setColor(color[1]);
		g.fillRect(x - 1, y - 1, 1, 1);
	}

	/**
	 * Draw a rectangle that contains a circular gradient
	 * 
	 * @param g
	 *            Graphics Context
	 * @param startColor
	 *            Starting color of the gradient
	 * @param endColor
	 *            Ending color of the gradient
	 * @param x
	 *            coordinate of the rectangle
	 * @param y
	 *            coordinate of the rectangle
	 * @param width
	 *            of the rectangle
	 * @param height
	 *            of the rectangle
	 * 
	 */
	public final static void drawCircularGradientRect(final Graphics g, final int startColor, final int endColor,
			int x, int y, int width, int height) {
		int prevColor = g.getColor();
		final int[] color = com.aphrodite.image.ColorUtil.getGradient(startColor, endColor, width / 2);
		drawCircGradRect(g, color, x, y, width, height);
		g.setColor(prevColor);
	}

	/**
	 * Draw a circular gradient rectangle with alpha transparency.
	 * <p>
	 * Its a slower method compared to its other over load.
	 * 
	 * @see GraphicUtil#drawCircularGradientRect(Graphics, int, int, int, int,
	 *      int, int) </p>
	 * @param g
	 *            Graphics context
	 * @param startColor
	 *            Starting color of the gradient
	 * @param endColor
	 *            Ending color of the gradient
	 * @param alphaLevel
	 *            The transparency between 0 (fully transparent) and 255 (fully
	 *            opaque)
	 * @param x
	 *            coordinate of the rectangle
	 * @param y
	 *            coordinate of the rectangle
	 * @param width
	 *            of the rectangle
	 * @param height
	 *            of the rectangle
	 */
	public final static void drawCircularGradientRect(final Graphics g, final int startColor, final int endColor,
			int alphaLevel, int x, int y, int width, int height) {
		// int prevColor = g.getColor();
		// int steps;
		// if (width > height) {
		// steps = (height / 2) + (width - height);
		// } else {
		// steps = (width / 2) + (height - width);
		// }
		int[] color = com.aphrodite.image.ColorUtil.getGradient(startColor, endColor, width > height ? width / 2
				: height / 2);
		ColorUtil.setTransparency(color, alphaLevel);
		int[][] twoD = new int[height][width];
		int nRow = 0;
		int nCol = 0;
		int nRow2 = 0;
		int nCol2 = 0;
		int nMax = (height % 2 == 0) ? (height / 2) - 1 : height / 2;
		for (int clr = 0; clr < color.length; clr++) {
			for (int l1 = clr; l1 <= (height - (clr + 1)); l1++) {
				nRow = l1;
				nCol = clr;
				twoD[nRow][nCol] = color[clr];
				if (height - nRow - 1 >= 0) {
					nRow2 = height - nRow - 1;
					if (width - clr - 1 > 0) {
						nCol2 = width - clr - 1;
						twoD[nRow2][nCol2] = color[clr];
					}
				}
			}
			for (int l2 = clr; l2 <= (width - (clr + 1)); l2++) {
				nRow = clr;
				nCol = l2;
				twoD[nRow][nCol] = color[clr];
				if (width - nCol - 1 >= 0) {
					nCol2 = width - nCol - 1;
					if (height - clr - 1 >= 0) {
						nRow2 = height - clr - 1;
					}
					twoD[nRow2][nCol2] = color[clr];
				}
			}
		}

		int[] rgb = new int[width * height];
		// for (int i = 0; i < twoD.length; i++) {
		// for (int j = 0; j < twoD[0].length; j++) {
		// rgb[i * width + j] = twoD[i][j];
		// }
		// }

		for (int i = 0; i < height; i++) {
			// System.out.println("\nCurrent Row " + i);

			for (int j = 0; j < width; j++) {
				rgb[i * width + j] = twoD[i][j];
				// System.out.print("," + twoD[i][j]);
			}
		}

		g.drawRGB(rgb, 0, width, x, y, width, height, true);
		// drawCircGradRect(g, color, x, y, width, height);
		// g.setColor(prevColor);
	}

	/**
	 * 
	 * @param g
	 * @param gradient
	 *            Type
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 */
	public final static void drawGradient(Graphics g, Gradient gradient, int x, int y, int width, int height) {
		/* if its an horizontal/vertical split colors */
		if ((gradient.getType() & Gradient.TYPE_SPLIT) == Gradient.TYPE_SPLIT) {
			GraphicUtil.fillRect(g, gradient.getStartColor(), gradient.getEndColor(),
					(gradient.getType() & Gradient.TYPE_VERTICAL) == Gradient.TYPE_VERTICAL, x, y, width, height);
			/* if its a horizontal/vertical split gradient */
		} else if ((gradient.getType() & Gradient.TYPE_CENTERED) == Gradient.TYPE_CENTERED) {
			GraphicEffects.drawCenteredGradientRect(g, gradient.getStartColor(), gradient.getEndColor(), (gradient
					.getType() & Gradient.TYPE_VERTICAL) == Gradient.TYPE_VERTICAL, x, y, width, height);
		} else
			switch (gradient.getType()) {
			case Gradient.TYPE_RADIAL:
				drawCircularGradientRect(g, gradient.getStartColor(), gradient.getEndColor(), x, y, width, height);
				break;
			case Gradient.TYPE_VERTICAL:
				drawGradientRect(g, gradient.getStartColor(), gradient.getEndColor(), true, x, y, width, height);
				break;
			case Gradient.TYPE_RAINBOW:
				g.drawRGB(GraphicEffects.getRainbowGradient(width, height), 0, width, x, y, width, height, false);
				break;
			/* draw horizontal gradient */
			default:
				drawGradientRect(g, gradient.getStartColor(), gradient.getEndColor(), false, x, y, width, height);
			}

	}

	/**
	 * Draw a gradient rectangle
	 * 
	 * <p>
	 * <table>
	 * <tr>
	 * <td><img src=../../../resources/grad_horz.png></td>
	 * <td><img src=../../../resources/grad_vert.png></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <code>startColor</code>=0<br/><code>endColor</code>=0xffffff<br/><code>isVertical</code>=false
	 * <td>
	 * <td>
	 * <code>startColor</code>=0<br/><code>endColor</code>=0xffffff<br/><code>isVertical</code>=true
	 * <td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param g
	 *            Graphics context
	 * @param startColor
	 *            of the gradient
	 * @param endColor
	 *            of the gradient
	 * @param isVertical
	 *            is a vertical or horizontal gradient
	 * @param x
	 *            coordinate of the rectangle
	 * @param y
	 *            coordinate of the rectangle
	 * @param width
	 *            of the rectangle
	 * @param height
	 *            of the rectangle
	 */
	public final static void drawGradientRect(Graphics g, int startColor, int endColor, boolean isVertical, int x,
			int y, int width, int height) {
		if (width == 0 || height == 0)
			return;
		/* get the previous color of the graphics primitive */
		final int prevColor = g.getColor();
		final int[] colors = com.aphrodite.image.ColorUtil.getGradient(startColor, endColor, isVertical ? width
				: height);
		for (int i = 0; i < colors.length; i++) {
			g.setColor(colors[i]);
			if (isVertical)
				g.drawLine(x + i, y, x + i, y + height - 1);
			else
				g.drawLine(x, y + i, x + width - 1, y + i);
		}
		g.setColor(prevColor);

	}

	/**
	 * Draw a gradient rectangle with transparency
	 * <p>
	 * <table>
	 * <tr>
	 * <td><img src=../../../resources/grad_alpha.png></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <code>startColor</code>=0<br/><code>endColor</code>=0xffffff<br/><code>alphaLevel</code>
	 * 180<br/><code>isVertical</code>=true
	 * <td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param g
	 *            Graphics context
	 * @param startColor
	 *            of the gradient
	 * @param endColor
	 *            of the gradient
	 * @param alphaLevel
	 *            The transparency between 0 (fully transparent) and 255 (fully
	 *            opaque)
	 * @param isVertical
	 *            is a vertical or horizontal gradient
	 * @param x
	 *            coordinate of the rectangle
	 * @param y
	 *            coordinate of the rectangle
	 * @param width
	 *            of the rectangle
	 * @param height
	 *            of the rectangle
	 */
	public static void drawGradientRect(final Graphics g, final int startColor, final int endColor,
			final int alphaLevel, boolean isVertical, int x, int y, int width, int height) {
		int prevColor = g.getColor();
		final int[] colors = com.aphrodite.image.ColorUtil.getGradient(startColor, endColor, isVertical ? width
				: height);
		for (int i = 0; i < colors.length; i++) {
			colors[i] = com.aphrodite.image.ColorUtil.setTransparency(colors[i], alphaLevel);
		}
		int[] argb = new int[width * height];
		if (!isVertical) {// horizontal
			for (int i = 0; i < height; i++) {
				for (int j = 0; j < width; j++) {
					argb[i * width + j] = colors[i];
				}
			}
		} else {// vertical
			for (int i = 0; i < width; i++) {
				for (int j = 0; j < height; j++) {
					argb[j * height + i] = colors[i];
				}
			}
		}

		g.drawRGB(argb, 0, width, x, y, width, height, true);
		g.setColor(prevColor);
	}

	/**
	 * Draw a Rainbow gradient
	 * 
	 * @param width
	 * @param height
	 * @return
	 */
	public final static int[] getRainbowGradient(final int width, final int height) {
		final int[] data = new int[width * height];
		int index = 0;
		for (int i = 0; i < height; i++) {
			int red = (i * 255) / (height - 1);
			for (int j = 0; j < width; j++) {
				int green = (j * 255) / (width - 1);
				int blue = 128;
				data[index++] = (red << 16) | (green << 8) | blue;
			}
		}
		return data;
	}

	private GraphicEffects() {}

}
